package com.easy.frame.servers.sys.controller;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.easy.frame.base.basic.ServiceResponse;
import com.easy.frame.base.basic.constant.DbConstants;
import com.easy.frame.base.basic.enums.ResponseCodeEnum;
import com.easy.frame.base.basic.pojo.vo.BaseTreeVO;
import com.easy.frame.base.basic.pojo.vo.DynamicMenuVO;
import com.easy.frame.base.basic.pojo.vo.SysStaffAllVO;
import com.easy.frame.base.basic.util.AuthUtils;
import com.easy.frame.base.service.tree.TreeHandler;
import com.easy.frame.base.service.tree.TreeUtil;
import com.easy.frame.common.redis.RedisUtil;
import com.easy.frame.servers.sys.entity.SysMenu;
import com.easy.frame.servers.sys.entity.SysStaff;
import com.easy.frame.servers.sys.mapper.menu.SysMenuMapper;
import com.easy.frame.servers.sys.mapping.SysMenuMapping;
import com.easy.frame.servers.sys.service.role.SysRoleMenuService;
import com.easy.frame.servers.sys.service.staff.SysStaffRoleService;
import com.easy.frame.servers.sys.service.staff.SysStaffService;
import com.easy.frame.servers.sys.util.RedisServer4Role;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 登录接口类
 *
 * @author zzf
 * @date 2020/7/8 14:02
 */
@RestController
public class LoginController {

    @Autowired
    SysStaffService staffService;

    @Resource
    SysMenuMapper sysMenuMapper;

    @Autowired
    SysStaffRoleService sysStaffRoleService;

    @Autowired
    SysRoleMenuService sysRoleMenuService;

    /**
     * 根据账号密码员工租户id获取该用户的菜单权限（即左边栏的菜单表），至于当前员工的菜单功能权限是在获取员工对应菜单权限中获取；
     * 另外附增该员工对应的项目时间过期告警提醒
     */
    @ApiImplicitParams({
            @ApiImplicitParam(value = "租户id", name = "tenantId"),
            @ApiImplicitParam(value = "用户名", name = "username"),
            @ApiImplicitParam(value = "密码", name = "password"),
            @ApiImplicitParam(value = "是否记住自己", name = "rememberMe")
    })
    @ApiOperation("用户登录接口")
    @PostMapping("/login")
    @CrossOrigin
    public ServiceResponse<SysStaffAllVO> login(@RequestParam Long tenantId,
                                                @RequestParam String username,
                                                @RequestParam String password,
                                                @RequestParam(required = false, defaultValue = "true") boolean rememberMe) {
        SysStaff staff = staffService.getOne(new LambdaQueryWrapper<SysStaff>()
                .eq(SysStaff::getTenantId, tenantId)
                .eq(SysStaff::getUsername, username)
                .eq(SysStaff::getPassword, password)
                .eq(SysStaff::getEnabled, DbConstants.DEFAULT_YES)
                .select(SysStaff::getId, SysStaff::getStatus)
        );
        if (staff == null) {
            return new ServiceResponse<>(ResponseCodeEnum.FAIL.getCode(), "用户名不存在（已失效）或密码错误！");
        }
        if (DbConstants.STAFF_STATUS_EXPIRED.equals(staff.getStatus())) {
            return new ServiceResponse<>(ResponseCodeEnum.FAIL.getCode(), "该用户已过期，请联系管理员！");
        }
        if (DbConstants.STAFF_STATUS_DISABLED.equals(staff.getStatus())) {
            return new ServiceResponse<>(ResponseCodeEnum.FAIL.getCode(), "该用户已被禁用，请联系管理员！");
        }

        SysStaffAllVO staffAllVO = staffService.getAllInfoById(staff.getId());

        if (ObjectUtil.isEmpty(staffAllVO.getMenuList())) {
            return new ServiceResponse<>(ResponseCodeEnum.FAIL.getCode(), "该用户没有可用权限，请联系管理员！");
        }
        // 缓存用户菜单权限
        RedisServer4Role.setUserMenus(staffAllVO);

        // 需要将菜单改成树状图结构
        menuTreeHandle(staffAllVO);

        String token = AuthUtils.generateToken(staffAllVO.getId(), staffAllVO.getLanguage(), staffAllVO.getTenantId(), rememberMe);
        staffAllVO.setAccessToken(token);
        // redis控制项目过期告警消息
        warningRedis(staffAllVO);
        return new ServiceResponse<>(staffAllVO, ResponseCodeEnum.SUCCESS);
    }

    /**
     * 将菜单转成树状图结构
     * 适配前端的动态菜单
     */
    private void menuTreeHandle(SysStaffAllVO staffAllVO) {
        List<SysStaffAllVO.Menu> menuList = staffAllVO.getMenuList();
        // 获取所有底层菜单path集合
        List<String> pathList = menuList.stream().map(SysStaffAllVO.Menu::getPath).collect(Collectors.toList());
        // 获取其所有父级菜单path
        Set<String> parentPath = TreeUtil.getAllParentPath(pathList);
        // 查询所有父级菜单数据
        List<SysMenu> sysMenuList = sysMenuMapper.selectListByPaths(parentPath);
        // 实体转换并插入到集合
        menuList.addAll(SysMenuMapping.INSTANCE.listPo2vo(sysMenuList, staffAllVO.getLanguage()));
        // 转成树状结构
        BaseTreeVO tree = TreeHandler.of(DynamicMenuVO.class).getTree(menuList);
        staffAllVO.setDynamicMenu(tree);
    }

    /**
     * redis控制项目过期告警消息
     */
    public void warningRedis(SysStaffAllVO staffAllVO) {
        Long warning = RedisUtil.getInstance().increment("warning", staffAllVO.getId().toString(), 1);
        //1.如果warning为1，那就说明是当天第一次登陆进来，另外会启用一个定时任务去清理当天0点所有的redis为warning的hash值
        //2.查询当天有哪些项目即将过期，不同的用户，相同的租户都会有告警信息，所以warning == 1是针对于当前用户登陆次数
        if (warning == 1 && staffAllVO.getTenantId() != 0) {
            List<String> overdueProjectNames = staffService.getOverduePro(staffAllVO.getTenantId());
            staffAllVO.setProjectOverdueList(overdueProjectNames);
        }
    }


    @ApiOperation("0点告警提醒")
    @PostMapping("/warning")
    public List<String> login(@RequestParam Long tenantId) {
        String staffId = AuthUtils.getCurrentUserId().toString();
        RedisUtil.getInstance().increment("warning", staffId, 1);
        return staffService.getOverduePro(tenantId);
    }
}
